package ch.ethz.eai.data;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * "Database" for storing weather updates.
 * @author zistvan
 *
 */
public class StatusStore {
		
	private List<WeatherObject> reports = new LinkedList<WeatherObject>();
	
	/**
	 * Save weather status for a location. <br>
	 * <i>Important:</i> coordinates are mandatory in location.
	 * <br>
	 * Returns true if the status could be recorded.
	 */
	public synchronized boolean save(WeatherStatus status) {
		if (status.getLocation().hasCoordinates()) {
			reports.add(new WeatherObject(status.getLocation().getLongtitude(), status.getLocation().getLatitude(), status.getStatus(), status.getReportedTime()));
			return true;
		}
		
		return false;
	}
	
	/**
	 * Returns the majority vote on how the weather will be in the vicinity of a given location.
	 * <br> 
	 * Only statuses that has been submitted at least an hour ago are taken into consideration.
	 *  <br><i>Important:</i> coordinates are mandatory in location.
	 *  <br>
	 * In case something is not right, {@link WeatherStatus}.TYPE_UNKNOWN is returned.
	 */
	public synchronized StatusResponse majorityFor(Location loc, double radius) {
		
			//ugly KLUDGE: see that this may not work at midnight :P
			Date anHourAgo = new Date();
			anHourAgo.setHours(new Date().getHours()-1);
			
			HashMap<Integer, Integer> typeCount = new HashMap<Integer, Integer>();
			typeCount.put(WeatherStatus.STAT_CLOUDS, 0);
			typeCount.put(WeatherStatus.STAT_RAIN, 0);
			typeCount.put(WeatherStatus.STAT_SNOW, 0);
			typeCount.put(WeatherStatus.STAT_CLEAR, 0);
			typeCount.put(WeatherStatus.STAT_STORM, 0);

			Iterator<WeatherObject> i = reports.iterator();
			while (i.hasNext()) {
				WeatherObject cur = i.next();
				
				if (cur.reported.before(anHourAgo)) {
					//do cleanup of list
					i.remove();
				} else {
					if (distanceBetween(cur, loc)<=radius) {
						typeCount.put(cur.type, typeCount.get(cur.type)+1);
					}
				}
			}
			
			int maxType = WeatherStatus.STAT_CLEAR;
			int maxCount = 0;
			int totalCount = 0;
			for (int type : typeCount.keySet()) {
				totalCount+=typeCount.get(type);
				if (typeCount.get(type)>maxCount) {
					maxCount = typeCount.get(type);
					maxType = type;
				}
			}
			
			if (maxCount>0) {
				return new StatusResponse(maxType, totalCount, maxCount);
			}
		
		return new StatusResponse(WeatherStatus.STAT_UNKNOWN, 0, 0);
	}

	
	private static double distanceBetween(WeatherObject cur, Location loc) {
		double lo1 = cur.longtitude;
		double la1 = cur.latitude;
		double lo2 = loc.getLongtitude();
		double la2 = loc.getLatitude();
		
		return Math.sqrt((lo1-lo2)*(lo1-lo2)+(la2-la1)*(la2-la1));
	}


	private class WeatherObject {
		public int type;
		public Date reported;
		private double longtitude = 0;
		private double latitude = 0;
		
		public WeatherObject(double longt, double latd, int type, Date reported) {
			super();
			this.type = type;
			this.reported = reported;
			this.longtitude = longt;
			this.latitude = latd;
		}
		
		
	}
}
